// File typer for Mac OS X
// Copyright 2006 (C) Ralph Thomas

#include <model/file/typerMac.h>
#include <vector>
#include "carbon_safe.hpp"

#define UNKNOWN_FILE	"x-mission/x-file"
#define BROKEN_TYPING	1

//
// The file typing used on the Macintosh is broken. All files on an HFS
// volume are supposed to have filetype identifiers (unique 32bit value);
// many of them actually might. Files on non HFS volumes have to be
// typed by extension, which is a *really* bad idea. At some point it would
// be a good idea to remove this code and replace it with the XDG library
// and put the magic database in the app bundle. We would still need to
// integrate with Apple's system though :(. At least with XDG we can say things
// like "any directory with a file called xyz inside is of this type..".
//

namespace model {
	std::string getFileType( const boost::filesystem::path& p ) {
		std::string path( p.native_file_string() );
		//
		// First get a string of the path, then create a URL to it,
		// then pass the URL to launch services to get the OSType.
		//
		CFStringRef str = CFStringCreateWithBytes( kCFAllocatorDefault,
			(const UInt8*)path.c_str(), path.size(), kCFStringEncodingUTF8,
			false );
		if( !str ) return UNKNOWN_FILE;
#ifdef BROKEN_TYPING
		CFURLRef url = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
			str, kCFURLPOSIXPathStyle, false );
		CFRelease( str );
		if( !url ) return UNKNOWN_FILE;
		//
		// Get the OSType out of Launch Services.
		//
		LSItemInfoRecord info;
		info.extension = 0;
		if( LSCopyItemInfoForURL( url,
				kLSRequestTypeCreator | kLSRequestExtension, &info ) != noErr )
		{
			CFRelease( url );
			return UNKNOWN_FILE;
		}
		CFRelease( url );
		CFStringRef ut = 0;
		//
		// The UTI system doesn't perform any file typing (which would be
		// useful), so we have to guess the type from the extension or use
		// the filetype if the file has one (most don't, depending on the
		// volume type).
		//
		if( info.extension ) {
			ut = UTTypeCreatePreferredIdentifierForTag(
				kUTTagClassFilenameExtension, info.extension, 0 );
			CFRelease( info.extension );
		}
		if( !ut ) {
			//
			// Now we need to get the Universal Type name for the OSType that
			// Launch Services knew about.
			//
			CFStringRef ostype = UTCreateStringForOSType( info.filetype );
			ut = UTTypeCreatePreferredIdentifierForTag( kUTTagClassOSType,
				ostype, 0 );
			CFRelease( ostype );
		}
		if( !ut )
			return UNKNOWN_FILE;
#else
		//
		// Mac OS 10.4 actually includes some filetyping functionality.
		// Amazing.
		//
		MDItemRef item = MDItemCreate( kCFAllocatorDefault, str );
		CFStringRef ut = (CFStringRef)MDItemCopyAttribute( item,
			kMDItemContentType );
		if( !ut )
			return UNKNOWN_FILE;
#endif
		//
		// Finally, we can get the MIME type which is associated with the
		// UTI type that we now have.
		//
		CFStringRef mime = UTTypeCopyPreferredTagWithClass( ut,
			kUTTagClassMIMEType );
		CFRelease( ut );
		if( !mime ) return UNKNOWN_FILE;
		//
		// Convert the CFString to a real STL string.
		//
		CFRange range;
		CFIndex size = 0;
		range.location = 0;
		range.length = CFStringGetLength( mime );
		CFStringGetBytes( mime, range, kCFStringEncodingUTF8, 0, false, 0,
			9999, &size );
		if( !size ) return UNKNOWN_FILE;
		std::vector<char> data( size );
		//
		// Nasty typecast required to get around the API not knowing that it
		// is converting to a signed type.
		//
		CFStringGetBytes( mime, range, kCFStringEncodingUTF8, 0, false,
			(UInt8*)&(data[0]), size, 0 );
		return std::string( &(data[0]), data.size() );
	}
}
